package main

import (
	"github.com/gomodule/redigo/redis"
	"log"
	"time"
)

func main() {
	// 使用方式
	InitRedis()
	testKey := "test_key"
	RedisLock(testKey)
	defer RedisUnLock(testKey)
	// do something
}

var RedisClient *RedisEngine

type RedisEngine struct {
	// Redis服务器地址
	Endpoints []string
	// auth
	Password string
	// 监控数据来源
	Client *redis.Pool
}

func InitRedis() {
	con := []string{"test-che02.rdb.58dns.org:50131"}
	var err error
	RedisClient, err = NewRedisEngine(con, "5a03e37c1461a649")
	if err != nil {
		log.Fatalf("Init redis server is err: %s", err.Error())
	}
}

func NewRedisEngine(eps []string, pwd string) (*RedisEngine, error) {
	client := &RedisEngine{
		Endpoints: eps,
		Password:  pwd,
	}
	client_pool := &redis.Pool{
		// 最大的空闲连接数，表示即使没有redis连接时依然可以保持N个空闲的连接，而不被清除，随时处于待命状态。
		MaxIdle: 10,
		// 最大的激活连接数，表示同时最多有N个激活连接
		MaxActive: 10,
		// 最大的空闲连接等待时间，超过此时间后，空闲连接将被关闭
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			// 连接
			conn, err := redis.Dial(
				eps[0],
				"tcp",
				//time.Second*5,
				//DialConnectTimeout(5*time.Second),
				//DialReadTimeout(5*time.Second),
				//DialWriteTimeout(5*time.Second)
			)
			if err != nil {
				log.Printf("redis dial failed: %s", err.Error())
				return nil, err
			}
			// 授权
			if pwd != "" {
				if _, err := conn.Do("AUTH", pwd); err != nil {
					conn.Close()
					log.Printf("redis auth failed: %s with password: %s",
						err.Error(), pwd)
					return nil, err
				}
			}
			// 选择DB
			// NIL
			// 返回可用连接
			return conn, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			if err != nil {
				log.Printf("redis ping failed: %s", err.Error())
			}
			return err
		},
	}
	client.Client = client_pool
	return client, nil
}

// 执行SET key val EX timeout NX， 成功返回值为"ok"，失败为 nil
func (de *RedisEngine) SetExNx(key string, val interface{}, timeout int) (reply interface{}, err error) {
	conn := de.Client.Get()
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	reply, err = conn.Do("SET", key, val, "EX", timeout, "NX")
	return reply, err
}

// 删除key，成功返回1，失败返回0
func (de *RedisEngine) Del(key string) int64 {
	conn := de.Client.Get()
	defer conn.Close()
	reply, err := conn.Do("DEL", key)
	if err != nil {
		log.Printf("Redis del key: %s failed with error: %s", key, err.Error())
		return 0
	}
	return reply.(int64)
}

// 加锁过程， 返回值代表是否获取到分布式锁
func RedisLock(key string) bool {
	reply, err := RedisClient.SetExNx(key, 1, 5)
	if err != nil || reply == nil {
		log.Println("Redis Lock failed: ", err)
		return false
	}
	log.Printf("Redis Lock key: %s at %s", key, time.Now().Format("2006-01-02 15:04:05.000"))
	return true
}

// 锁释放过程
func RedisUnLock(key string) {
	reply := RedisClient.Del(key)
	if reply != 1 {
		log.Println("Redis UnLock failed, key: ", key)
		return
	}
	log.Printf("Redis UnLock key: %s at %s", key, time.Now().Format("2006-01-02 15:04:05.000"))
}
